Obvladajte Reactov hook useFormState. Celovit vodnik za poenostavljeno upravljanje stanj obrazcev, strežniško validacijo in izboljšano uporabniško izkušnjo s Server Actions.
React useFormState: Poglobljen pregled sodobnega upravljanja in validacije obrazcev
Obrazci so temelj spletne interaktivnosti. Od preprostih kontaktnih obrazcev do zapletenih večstopenjskih čarovnikov so ključni za vnos uporabniških podatkov in njihovo pošiljanje. Razvijalci Reacta so se leta prebijali skozi pokrajino rešitev za upravljanje stanj, od preprostih useState hookov za osnovne scenarije do zmogljivih knjižnic tretjih oseb, kot sta Formik in React Hook Form, za bolj kompleksne potrebe. Čeprav so ta orodja odlična, se React nenehno razvija in ponuja bolj integrirane, zmogljivejše primitive.
Tukaj nastopi useFormState, hook, predstavljen v Reactu 18. Prvotno zasnovan za brezhibno delovanje z React Server Actions, useFormState ponuja poenostavljen, robusten in nativen pristop k upravljanju stanj obrazcev, še posebej pri delu s strežniško logiko in validacijo. Poenostavlja postopek prikaza povratnih informacij s strežnika, kot so napake pri validaciji ali sporočila o uspehu, neposredno v vašem uporabniškem vmesniku.
Ta celovit vodnik vas bo popeljal v globine hooka useFormState. Raziskali bomo njegove osrednje koncepte, praktične implementacije, napredne vzorce in kako se umešča v širši ekosistem sodobnega razvoja z Reactom. Ne glede na to, ali gradite aplikacije z Next.js, Remixom ali čistim Reactom, vam bo razumevanje useFormState dalo močno orodje za izgradnjo boljših in odpornejših obrazcev.
Kaj je `useFormState` in zakaj ga potrebujemo?
V svojem bistvu je useFormState hook, zasnovan za posodabljanje stanja na podlagi rezultata akcije obrazca. Predstavljajte si ga kot specializirano različico useReducer, prilagojeno posebej za pošiljanje obrazcev. Elegantno premošča vrzel med interakcijo uporabnika na odjemalski strani in obdelavo na strežniški strani.
Pred useFormState je tipičen potek pošiljanja obrazca, ki vključuje strežnik, morda izgledal takole:
- Uporabnik izpolni obrazec.
- Stanje na odjemalski strani (npr. z uporabo
useState) sledi vrednostim vnosnih polj. - Ob oddaji dogodek
onSubmitprepreči privzeto obnašanje brskalnika. - Ročno se zgradi in pošlje
fetchzahtevek na strežniški API. - Upravljajo se stanja nalaganja (npr.
const [isLoading, setIsLoading] = useState(false)). - Strežnik obdela zahtevek, izvede validacijo in komunicira z bazo podatkov.
- Strežnik vrne JSON odgovor (npr.
{ success: false, errors: { email: 'Invalid format' } }). - Koda na odjemalski strani razčleni ta odgovor in posodobi drugo spremenljivko stanja za prikaz napak ali sporočil o uspehu.
Ta postopek, čeprav deluje, vključuje precej odvečne kode za upravljanje stanj nalaganja, stanj napak in cikla zahtevek/odgovor. useFormState, še posebej v kombinaciji s Server Actions, to dramatično poenostavi z ustvarjanjem bolj deklarativnega in integriranega poteka.
Glavne prednosti uporabe useFormState so:
- Brezhibna integracija s strežnikom: Je nativna rešitev za obravnavo odgovorov iz Server Actions, s čimer postane strežniška validacija prvovrstni del vaše komponente.
- Poenostavljeno upravljanje stanj: Centralizira logiko za posodobitve stanja obrazca, kar zmanjšuje potrebo po več
useStatehookih za podatke, napake in stanje oddaje. - Postopno izboljšanje: Obrazci, zgrajeni z
useFormStatein Server Actions, lahko delujejo tudi, če je JavaScript na odjemalcu onemogočen, saj temeljijo na standardnih HTML oddajah obrazcev. - Izboljšana uporabniška izkušnja: Olajša takojšnje in kontekstualne povratne informacije uporabniku, kot so vgrajene napake pri validaciji ali sporočila o uspehu, neposredno po oddaji obrazca.
Razumevanje signature hooka `useFormState`
Da bi obvladali ta hook, si najprej poglejmo njegovo signaturo in povratne vrednosti. Je enostavnejši, kot se morda zdi na prvi pogled.
const [state, formAction] = useFormState(action, initialState);
Parametri:
action: To je funkcija, ki se bo izvedla ob oddaji obrazca. Ta funkcija prejme dva argumenta: prejšnje stanje obrazca in oddane podatke obrazca. Pričakuje se, da bo vrnila novo stanje. Običajno je to Server Action, vendar je lahko katera koli funkcija.initialState: To je vrednost, ki jo želite, da ima stanje obrazca na začetku, preden se je zgodila kakršna koli oddaja. Lahko je katera koli serializabilna vrednost (niz, število, objekt itd.).
Povratne vrednosti:
useFormState vrne polje z natančno dvema elementoma:
state: Trenutno stanje obrazca. Ob prvem prikazu bo toinitialState, ki ste ga posredovali. Po oddaji obrazca bo to vrednost, ki jo je vrnila vaša funkcijaaction. To stanje uporabite za prikaz povratnih informacij v uporabniškem vmesniku, kot so sporočila o napakah.formAction: Nova funkcija akcije, ki jo posredujeteactionlastnosti vašega elementa<form>. Ko se ta akcija sproži (z oddajo obrazca), bo React poklical vašo originalno funkcijoactions prejšnjim stanjem in podatki obrazca, nato pa posodobilstatez rezultatom.
Ta vzorec se vam morda zdi znan, če ste uporabljali useReducer. Funkcija action je kot reducer, initialState je začetno stanje, React pa za vas opravi pošiljanje (dispatching), ko je obrazec oddan.
Praktični prvi primer: Preprost obrazec za prijavo na novice
Zgradimo preprost obrazec za prijavo na e-novice, da vidimo useFormState v akciji. Imeli bomo eno vnosno polje za e-pošto in gumb za oddajo. Strežniška akcija bo izvedla osnovno validacijo, da preveri, ali je e-pošta vnesena in ali je v veljavni obliki.
Najprej definirajmo našo strežniško akcijo. Če uporabljate Next.js, jo lahko postavite v isto datoteko kot vašo komponento z dodajanjem direktive 'use server'; na vrh funkcije.
// In actions.js or at the top of your component file with 'use server'
export async function subscribe(previousState, formData) {
const email = formData.get('email');
if (!email) {
return { message: 'Email is required.' };
}
// A simple regex for demonstration purposes
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(email)) {
return { message: 'Please enter a valid email address.' };
}
// Here you would typically save the email to a database
console.log(`Subscribing with email: ${email}`);
// Simulate a delay
await new Promise(res => setTimeout(res, 1000));
return { message: 'Thank you for subscribing!' };
}
Sedaj pa ustvarimo odjemalsko komponento, ki uporablja to akcijo z useFormState.
'use client';
import { useFormState } from 'react-dom';
import { subscribe } from './actions';
const initialState = {
message: null,
};
export function SubscriptionForm() {
const [state, formAction] = useFormState(subscribe, initialState);
return (
<form action={formAction}>
<h3>Subscribe to Our Newsletter</h3>
<div>
<label htmlFor="email">Email Address</label>
<input type="email" id="email" name="email" required />
</div>
<button type="submit">Subscribe</button>
{state?.message && <p>{state.message}</p>}
</form>
);
}
Poglejmo si podrobneje, kaj se dogaja:
- Uvozimo
useFormStateizreact-dom(opomba: ne izreact). - Definiramo objekt
initialState. To zagotavlja, da ima naša spremenljivkastatekonsistentno obliko že od prvega prikaza. - Kličemo
useFormState(subscribe, initialState). S tem povežemo stanje naše komponente s strežniško akcijosubscribe. - Vrnjeni
formActionse posreduje lastnostiactionelementa<form>. To je čarobna povezava. - Pogojno izrišemo sporočilo iz našega objekta
state. Ob prvem prikazu jestate.messagenull, zato se nič ne prikaže. - Ko uporabnik odda obrazec, React kliče
formAction. To sproži našo strežniško akcijosubscribe. FunkcijasubscribeprejmepreviousState(na začetku našinitialState) informData. - Strežniška akcija izvede svojo logiko in vrne nov objekt stanja (npr.
{ message: 'Email is required.' }). - React prejme to novo stanje in ponovno izriše komponento
SubscriptionForm. Spremenljivkastatezdaj vsebuje nov objekt, in naš pogojni odstavek prikaže sporočilo o napaki ali uspehu.
To je izjemno zmogljivo. Implementirali smo celotno zanko validacije odjemalec-strežnik z minimalno odvečno kodo za upravljanje stanja na odjemalski strani.
Izboljšanje UX z `useFormStatus`
Naš obrazec deluje, vendar bi lahko bila uporabniška izkušnja boljša. Ko uporabnik klikne 'Subscribe', gumb ostane aktiven in ni vizualnega znaka, da se nekaj dogaja, dokler strežnik ne odgovori. Tu nastopi hook useFormStatus.
Hook useFormStatus zagotavlja informacije o stanju zadnje oddaje obrazca. Ključno je, da ga je treba uporabiti v komponenti, ki je otrok elementa <form>. Ne deluje, če ga kličemo v isti komponenti, ki izrisuje obrazec.
Ustvarimo ločeno komponento SubmitButton.
'use client';
import { useFormStatus } from 'react-dom';
export function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Subscribing...' : 'Subscribe'}
</button>
);
}
Sedaj lahko posodobimo naš SubscriptionForm, da uporabi to novo komponento:
// ... imports
import { SubmitButton } from './SubmitButton';
// ... initialState and other code
export function SubscriptionForm() {
const [state, formAction] = useFormState(subscribe, initialState);
return (
<form action={formAction}>
{/* ... form inputs ... */}
<SubmitButton /> {/* Replace the old button */}
{state?.message && <p>{state.message}</p>}
</form>
);
}
S to spremembo, ko je obrazec oddan, vrednost pending iz useFormStatus postane true. Naša komponenta SubmitButton se ponovno izriše, onemogoči gumb in spremeni njegovo besedilo v 'Subscribing...'. Ko se strežniška akcija zaključi in useFormState posodobi stanje, obrazec ni več v stanju 'pending', in gumb se vrne v prvotno stanje. To uporabniku zagotavlja bistvene povratne informacije in preprečuje podvojene oddaje.
Napredna validacija s strukturiranimi stanji napak in Zodom
En sam niz sporočila je v redu za preproste obrazce, vendar resnične aplikacije pogosto zahtevajo napake pri validaciji za vsako polje posebej. To lahko enostavno dosežemo z vračanjem bolj strukturiranega objekta stanja iz naše strežniške akcije.
Izboljšajmo našo akcijo, da bo vračala objekt s ključem errors, ki sam vsebuje sporočila za določena polja. To je tudi odlična priložnost za uvedbo knjižnice za validacijo shem, kot je Zod, za bolj robustno in vzdržljivo logiko validacije.
Korak 1: Namestite Zod
npm install zod
Korak 2: Posodobite strežniško akcijo
Ustvarili bomo Zod shemo, da definiramo pričakovano obliko in pravila validacije za naše podatke obrazca. Nato bomo uporabili schema.safeParse() za validacijo prihajajočega formData.
'use server';
import { z } from 'zod';
// Define the schema for our form
const contactSchema = z.object({
name: z.string().min(2, { message: 'Name must be at least 2 characters.' }),
email: z.string().email({ message: 'Invalid email address.' }),
message: z.string().min(10, { message: 'Message must be at least 10 characters.' }),
});
export async function submitContactForm(previousState, formData) {
const validatedFields = contactSchema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
message: formData.get('message'),
});
// If validation fails, return the errors
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
message: 'Validation failed. Please check your inputs.',
};
}
// If validation succeeds, process the data
// For example, send an email or save to a database
console.log('Success!', validatedFields.data);
// ... processing logic ...
// Return a success state
return {
errors: {},
message: 'Thank you for your message! We will get back to you soon.',
};
}
Opazite, kako uporabljamo validatedFields.error.flatten().fieldErrors. To je priročna Zodova funkcija, ki pretvori objekt napak v bolj uporabno strukturo, kot je: { name: ['Name must be at least 2 characters.'], message: ['Message is too short'] }.
Korak 3: Posodobite odjemalsko komponento
Sedaj bomo posodobili našo komponento obrazca, da bo obravnavala to strukturirano stanje napak.
'use client';
import { useFormState } from 'react-dom';
import { submitContactForm } from './actions';
import { SubmitButton } from './SubmitButton'; // Assuming we have a submit button
const initialState = {
message: null,
errors: {},
};
export function ContactForm() {
const [state, formAction] = useFormState(submitContactForm, initialState);
return (
<form action={formAction}>
<h2>Contact Us</h2>
<div>
<label htmlFor="name">Name</label>
<input type="text" id="name" name="name" />
{state.errors?.name && (
<p className="error">{state.errors.name[0]}</p>
)}
</div>
<div>
<label htmlFor="email">Email</label>
<input type="email" id="email" name="email" />
{state.errors?.email && (
<p className="error">{state.errors.email[0]}</p>
)}
</div>
<div>
<label htmlFor="message">Message</label>
<textarea id="message" name="message" />
{state.errors?.message && (
<p className="error">{state.errors.message[0]}</p>
)}
</div>
<SubmitButton />
{state.message && <p className="form-status">{state.message}</p>}
</form>
);
}
Ta vzorec je izjemno prilagodljiv in robusten. Vaša strežniška akcija postane edini vir resnice za logiko validacije, Zod pa zagotavlja deklarativen in tipovno varen način za definiranje teh pravil. Odjemalska komponenta preprosto postane porabnik stanja, ki ga zagotavlja useFormState, in prikazuje napake tam, kjer sodijo. Ta ločitev skrbi naredi kodo čistejšo, lažjo za testiranje in varnejšo, saj se validacija vedno izvaja na strežniku.
`useFormState` v primerjavi z drugimi rešitvami za upravljanje obrazcev
Z novim orodjem se pojavi vprašanje: "Kdaj naj uporabim to namesto tistega, kar že poznam?" Primerjajmo useFormState z drugimi pogostimi pristopi.
`useFormState` vs. `useState`
- `useState` je popoln za preproste, samo odjemalske obrazce ali ko morate izvesti zapletene, sprotne interakcije na odjemalski strani (kot je validacija v živo med tipkanjem) pred oddajo. Daje vam neposreden, natančen nadzor.
- `useFormState` se odlikuje, ko je stanje obrazca primarno določeno z odzivom strežnika. Zasnovan je za cikel zahtevek/odgovor pri oddaji obrazca in je prava izbira pri uporabi Server Actions. Odstrani potrebo po ročnem upravljanju fetch klicev, stanj nalaganja in razčlenjevanju odgovorov.
`useFormState` vs. Third-Party Libraries (React Hook Form, Formik)
Knjižnice, kot sta React Hook Form in Formik, so zrele, s funkcijami bogate rešitve, ki ponujajo celovit nabor orodij za upravljanje obrazcev. Zagotavljajo:
- Napredno validacijo na odjemalski strani (pogosto z integracijo shem za Zod, Yup itd.).
- Kompleksno upravljanje stanj za gnezdena polja, polja polj in več.
- Optimizacije delovanja (npr. izoliranje ponovnih izrisov samo na vnosna polja, ki se spremenijo).
- Pomočnike za nadzorovane komponente in integracijo z UI knjižnicami.
Torej, kdaj izbrati katero?
- Izberite
useFormState, ko:- Uporabljate React Server Actions in želite nativno, integrirano rešitev.
- Vaš primarni vir resnice za validacijo je strežnik.
- Cenite postopno izboljšanje in želite, da vaši obrazci delujejo brez JavaScripta.
- Logika vašega obrazca je relativno preprosta in osredotočena na cikel oddaje/odgovora.
- Izberite knjižnico tretje osebe, ko:
- Potrebujete obsežno in kompleksno validacijo na odjemalski strani s takojšnjimi povratnimi informacijami (npr. validacija ob izgubi fokusa ali ob spremembi).
- Imate zelo dinamične obrazce (npr. dodajanje/odstranjevanje polj, pogojna logika).
- Ne uporabljate ogrodja s Server Actions in gradite lastno plast za komunikacijo med odjemalcem in strežnikom z REST ali GraphQL API-ji.
- Potrebujete natančen nadzor nad zmogljivostjo in ponovnimi izrisi v zelo velikih obrazcih.
Pomembno je tudi omeniti, da se te možnosti med seboj ne izključujejo. Lahko uporabite React Hook Form za upravljanje stanja in validacije na odjemalski strani, nato pa z njegovim obravnavalcem oddaje pokličete Server Action. Vendar pa za mnoge pogoste primere uporabe kombinacija useFormState in Server Actions ponuja enostavnejšo in elegantnejšo rešitev.
Najboljše prakse in pogoste napake
Da bi kar najbolje izkoristili useFormState, upoštevajte naslednje najboljše prakse:
- Ohranite osredotočenost akcij: Vaša funkcija akcije obrazca naj bo odgovorna za eno stvar: obdelavo oddaje obrazca. To vključuje validacijo, mutacijo podatkov (shranjevanje v bazo podatkov) in vračanje novega stanja. Izogibajte se stranskim učinkom, ki niso povezani z izidom obrazca.
- Definirajte konsistentno obliko stanja: Vedno začnite z dobro definiranim
initialStatein zagotovite, da vaša akcija vedno vrne objekt z enako obliko, tudi ob uspehu. To preprečuje napake med izvajanjem na odjemalcu pri poskusu dostopa do lastnosti, kot jestate.errors. - Sprejmite postopno izboljšanje: Ne pozabite, da Server Actions delujejo brez odjemalskega JavaScripta. Oblikujte svoj uporabniški vmesnik tako, da elegantno obravnava oba scenarija. Na primer, zagotovite, da so sporočila o validaciji, izrisana na strežniku, jasna, saj uporabnik brez JS ne bo imel koristi od onemogočenega stanja gumba.
- Ločite skrbi uporabniškega vmesnika: Uporabite komponente, kot je naš
SubmitButton, za inkapsulacijo uporabniškega vmesnika, odvisnega od stanja. To ohranja vašo glavno komponento obrazca čistejšo in upošteva pravilo, da seuseFormStatusmora uporabljati v podrejeni komponenti. - Ne pozabite na dostopnost: Pri prikazovanju napak uporabite ARIA atribute, kot je
aria-invalid, na vaših vnosnih poljih in povežite sporočila o napakah z njihovimi ustreznimi vnosi z uporaboaria-describedby, da zagotovite, da so vaši obrazci dostopni uporabnikom bralnikov zaslona.
Pogosta napaka: Uporaba useFormStatus v isti komponenti
Pogosta napaka je klicanje useFormStatus v isti komponenti, ki izrisuje oznako <form>. To ne bo delovalo, ker mora biti hook znotraj konteksta obrazca, da lahko dostopa do njegovega stanja. Vedno izvlecite del vašega uporabniškega vmesnika, ki potrebuje stanje (kot je gumb), v svojo lastno podrejeno komponento.
Zaključek
Hook useFormState v sodelovanju s Server Actions predstavlja pomemben razvoj v načinu obravnave obrazcev v Reactu. Razvijalce usmerja k bolj robustnemu, strežniško osredotočenemu modelu validacije, hkrati pa poenostavlja upravljanje stanj na odjemalski strani. Z abstrakcijo zapletenosti življenjskega cikla oddaje nam omogoča, da se osredotočimo na najpomembnejše: definiranje naše poslovne logike in izgradnjo brezhibne uporabniške izkušnje.
Čeprav morda ne bo nadomestil celovitih knjižnic tretjih oseb za vsak primer uporabe, useFormState zagotavlja zmogljivo, nativno in postopno izboljšano podlago za veliko večino obrazcev v sodobnih spletnih aplikacijah. Z obvladovanjem njegovih vzorcev in razumevanjem njegovega mesta v ekosistemu Reacta lahko gradite bolj odporne, vzdržljive in uporabniku prijazne obrazce z manj kode in večjo jasnostjo.